package top.mcwebsite.markdown.render

import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.BasicText
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.TextStyle
import dev.snipme.highlights.Highlights
import dev.snipme.highlights.model.BoldHighlight
import dev.snipme.highlights.model.CodeHighlight
import dev.snipme.highlights.model.ColorHighlight
import dev.snipme.highlights.model.SyntaxLanguage
import dev.snipme.highlights.model.SyntaxTheme
import org.intellij.markdown.MarkdownTokenTypes
import org.intellij.markdown.ast.getTextInNode
import top.mcwebsite.markdown.model.MarkdownNode
import top.mcwebsite.markdown.theme.LocalMarkdownTheme

object CodeFenceComponent : MarkdownComponent {

    @Composable
    override fun render(markdownNode: MarkdownNode) {
        val theme = LocalMarkdownTheme.current
        val codeFenceTheme = theme.codeFenceTheme
        Box(
            modifier = Modifier.fillMaxWidth()
                .padding(theme.blockPadding)
                .background(color = codeFenceTheme.backgroundColor)
                .border(
                    width = codeFenceTheme.borderWidth,
                    brush = codeFenceTheme.borderColor,
                    shape = codeFenceTheme.borderShape
                )
                .padding(codeFenceTheme.codePadding)
        ) {
            val (language, codes) = parseCodeFence(markdownNode)
            println(language)
            val code = codes.fold(StringBuilder()) { acc, code -> acc.append(code) }.toString().trim()
            BasicText(buildAnnotationString(code, language, codeFenceTheme.theme, codeFenceTheme.codeTextStyle))
        }
    }

    /**
     * 解析代码块
     * @return language, code
     */
    private fun parseCodeFence(markdownNode: MarkdownNode): Pair<String, List<String>> {
        var language = ""
        val codes = mutableListOf<String>()
        markdownNode.node.children.forEach { child ->
            when (child.type) {
                MarkdownTokenTypes.FENCE_LANG -> {
                    language = child.getTextInNode(markdownNode.content).toString()
                }

                MarkdownTokenTypes.CODE_FENCE_CONTENT -> {
                    codes.add(child.getTextInNode(markdownNode.content).toString())
                }

                MarkdownTokenTypes.EOL -> {
                    codes.add("\n")
                }
            }
        }
        return language to codes
    }

    private fun buildAnnotationString(
        code: String,
        language: String,
        syntaxTheme: SyntaxTheme,
        codeTextStyle: TextStyle
    ): AnnotatedString {
        return Highlights.Builder()
            .code(code)
            .theme(syntaxTheme)
            .language(getSyntaxLanguage(language))
            .build()
            .getHighlights()
            .toAnnotatedString(code, codeTextStyle)
    }

    private fun getSyntaxLanguage(language: String): SyntaxLanguage {
        return when (language.lowercase()) {
            "c" -> SyntaxLanguage.C
            "cpp", "c++" -> SyntaxLanguage.CPP
            "dart" -> SyntaxLanguage.DART
            "java" -> SyntaxLanguage.JAVA
            "kotlin" -> SyntaxLanguage.KOTLIN
            "rust" -> SyntaxLanguage.RUST
            "C#", "c-sharp" -> SyntaxLanguage.CSHARP
            "coffeescript" -> SyntaxLanguage.COFFEESCRIPT
            "javascript" -> SyntaxLanguage.JAVASCRIPT
            "perl" -> SyntaxLanguage.PERL
            "python" -> SyntaxLanguage.PYTHON
            "ruby" -> SyntaxLanguage.RUBY
            "shell", "sh" -> SyntaxLanguage.SHELL
            "swift" -> SyntaxLanguage.SWIFT
            "typescript" -> SyntaxLanguage.TYPESCRIPT
            "go" -> SyntaxLanguage.GO
            "php" -> SyntaxLanguage.PHP
            else -> SyntaxLanguage.DEFAULT
        }
    }

    private fun List<CodeHighlight>.toAnnotatedString(code: String, codeTextStyle: TextStyle): AnnotatedString {
        val boldSpanStyle = codeTextStyle.toSpanStyle().copy(fontWeight = androidx.compose.ui.text.font.FontWeight.Bold)
        return AnnotatedString(
            text = code,
            spanStyles = this.map {
                when (it) {
                    is BoldHighlight -> {
                        AnnotatedString.Range(
                            item = boldSpanStyle,
                            start = it.location.start,
                            end = it.location.end,
                        )
                    }

                    is ColorHighlight -> {
                        AnnotatedString.Range(
                            item = codeTextStyle.toSpanStyle().copy(color = Color(it.rgb).copy(alpha = 1.0f)),
                            start = it.location.start,
                            end = it.location.end
                        )
                    }
                }

            }
        )
    }
}